              package com.java.diagnostics.visualizer.parser.vgc.j9;
              
              import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
              import com.java.diagnostics.visualizer.parser.xml.GCAndMemoryVisualizerXMLParser;
              import com.java.diagnostics.visualizer.sources.Source;
              import java.io.BufferedReader;
              import java.io.IOException;
              import java.util.ArrayList;
              import java.util.List;
              import org.xml.sax.Attributes;
              import org.xml.sax.helpers.AttributesImpl;
              
              public class FriendlyXMLParser
              {
                private static final String CLOSING_SLASH = "/";
                private static final String DOUBLE_QUOTE = "\"";
                private static final String ATTRIBUTE_EQUALS = "=";
                private static final char SPACE = ' ';
                private static final String END_TAG = ">";
                private static final String OPEN_TAG = "<";
                public static final String FILE_ELEMENT_NAME = "file";
                private List<String> openStack = new ArrayList();
                private GCAndMemoryVisualizerXMLParser callback;
              
                public void parse(Source source, GCAndMemoryVisualizerXMLParser instanceVGCXMLParser)
                  throws GCAndMemoryVisualizerException, Exception
                {
                  this.callback = instanceVGCXMLParser;
                  BufferedReader reader = source.getSafeBufferedStreamReader();
              
                  this.callback.startElement(null, null, "file", null);
                  try {
                    String line = extendLineToNextClosingTag(reader);
                    while (line != null)
                    {
                      int tagStart = line.indexOf("<");
                      int tagEnd = line.indexOf(">", tagStart + 1);
              
                      tagStart = line.lastIndexOf("<", tagEnd);
              
                      while ((tagStart != -1) && (tagEnd != -1) && (tagStart < tagEnd))
                      {
                        String body = line.substring(tagStart + 1, tagEnd + 1);
              
                        if (body.length() > 0) {
                          if (!body.startsWith("/"))
                          {
                            callOpenElement(body);
                          }
                          else {
                            callCloseElement(body);
                          }
                        }
              
                        tagStart = line.indexOf("<", tagEnd + 1);
                        tagEnd = line.indexOf(">", tagStart + 1);
                      }
              
                      line = extendLineToNextClosingTag(reader);
                    }
                  }
                  catch (IOException localIOException)
                  {
                  }
                  instanceVGCXMLParser.endElement(null, null, "file");
                }
              
                private String extendLineToNextClosingTag(BufferedReader reader)
                  throws IOException
                {
                  String line = reader.readLine();
                  if (line == null) {
                    return line;
                  }
                  boolean balanced = false;
                  int tagStart = -1;
                  while (!balanced)
                  {
                    tagStart = line.indexOf("<", tagStart + 1);
                    int oldTagStart = tagStart;
                    int tagEnd = line.indexOf(">", tagStart + 1);
              
                    if (tagEnd != -1)
                      tagStart = line.lastIndexOf("<", tagEnd);
                    else {
                      tagStart = oldTagStart;
                    }
              
                    if ((tagStart == -1) && (tagEnd == -1))
                    {
                      balanced = true;
                    }
                    else if (tagEnd > line.lastIndexOf("<"))
                    {
                      balanced = true;
                    }
                    else if (tagStart >= line.lastIndexOf("<"))
                    {
                      String nextLine = reader.readLine();
                      if (nextLine == null)
                      {
                        balanced = true;
                      }
                      else
                      {
                        tagStart--;
                        line = line.concat(nextLine);
                      }
                    }
                  }
                  return line;
                }
              
                private void callOpenElement(String body)
                {
                  int firstSpaceIndex = body.indexOf(' ');
              
                  if (firstSpaceIndex == -1) {
                    firstSpaceIndex = body.length();
                  }
              
                  String elementName = body.substring(0, firstSpaceIndex);
                  if (elementName.endsWith(">")) {
                    elementName = elementName.substring(0, elementName.length() - 1);
                  }
                  Attributes attrs = getAttributes(body.substring(firstSpaceIndex));
                  try
                  {
                    this.callback.startElement(null, null, elementName, attrs);
                    if (body.endsWith("/>"))
                      this.callback.endElement(null, null, elementName);
                    else
                      this.openStack.add(elementName);
                  }
                  catch (Exception localException)
                  {
                  }
                }
              
                private void callCloseElement(String body)
                {
                  int firstSpaceIndex = body.indexOf(' ');
              
                  if (firstSpaceIndex == -1) {
                    firstSpaceIndex = body.length();
                  }
              
                  String elementName = body.substring(0, firstSpaceIndex);
              
                  if (elementName.length() > 1)
                    elementName = elementName.substring(1, elementName.length() - 1);
                  else {
                    return;
                  }
              
                  boolean opened = false;
                  try {
                    for (int i = this.openStack.size() - 1; i >= 0; i--) {
                      String openElement = (String)this.openStack.get(i);
              
                      if (openElement.equals(elementName)) {
                        opened = true;
                        this.openStack.remove(i);
                        break;
                      }
              
                      this.callback.endElement(null, null, openElement);
                    }
                    if (!opened)
                    {
                      this.callback.startElement(null, null, elementName, 
                        new AttributesImpl());
                    }
                    this.callback.endElement(null, null, elementName);
                  }
                  catch (Exception localException)
                  {
                  }
                }
              
                private static Attributes getAttributes(String attributesString)
                {
                  AttributesImpl attrs = new AttributesImpl();
              
                  int quoteCount = countQuotes(attributesString);
                  if (quoteCount % 2 != 0) {
                    return attrs;
                  }
              
                  int attrStartIndex = 0;
              
                  int attrEndIndex = advanceAttrEndIndex(attributesString, 
                    attrStartIndex + 1);
                  while ((attrStartIndex != -1) && (attrEndIndex != -1) && 
                    (attrStartIndex < attrEndIndex))
                  {
                    String fragment = attributesString.substring(attrStartIndex + 1, 
                      attrEndIndex);
              
                    int nameEnd = fragment.indexOf("=");
                    if (nameEnd > 0)
                    {
                      String name = fragment.substring(0, nameEnd);
                      int quotesStart = fragment.indexOf("\"", nameEnd);
                      int quotesEnd = fragment.lastIndexOf("\"");
              
                      while (quotesEnd == quotesStart)
                      {
                        attrEndIndex = advanceAttrEndIndex(attributesString, 
                          attrEndIndex);
                        if (attrEndIndex == -1)
                        {
                          return attrs;
                        }
                        fragment = attributesString.substring(attrStartIndex + 1, 
                          attrEndIndex);
              
                        quotesEnd = fragment.lastIndexOf("\"");
                      }
              
                      String value = fragment.substring(quotesStart + 1, quotesEnd);
              
                      attrs.addAttribute(null, null, name, null, value);
                    }
              
                    attrStartIndex = attrEndIndex;
                    attrEndIndex = advanceAttrEndIndex(attributesString, 
                      attrStartIndex + 1);
                  }
                  return attrs;
                }
              
                private static int advanceAttrEndIndex(String attributesString, int originalAttrEndIndex)
                {
                  int attrEndIndex = attributesString.indexOf(' ', 
                    originalAttrEndIndex + 1);
                  if (attrEndIndex == -1)
                  {
                    attrEndIndex = attributesString.lastIndexOf("\">");
              
                    if (attrEndIndex == -1) {
                      attrEndIndex = attributesString.lastIndexOf("/>");
                    }
              
                    if (attrEndIndex != -1) {
                      attrEndIndex++;
                    }
                  }
                  if (attrEndIndex != originalAttrEndIndex) {
                    return attrEndIndex;
                  }
                  return -1;
                }
              
                private static int countQuotes(String attributesString)
                {
                  int startIndex = 0;
                  int quoteCount = -1;
                  int index = 0;
                  while (index > -1) {
                    quoteCount++;
                    index = attributesString.indexOf("\"", startIndex);
                    startIndex = index + 1;
                  }
                  return quoteCount;
                }
              }

